/**
 * 全局消息工具单例
 */
export class GEventManager {
    private _eventHandlers: { [key: string]: { callback: (...data: unknown[]) => void, target: unknown, once: boolean, }[] }

    init(): void {
        this._eventHandlers = cc.js.createMap()
    }

    // ====================================================================================
    // ===================================== 内部接口 ======================================
    // ====================================================================================

    // ====================================================================================
    // ===================================== 外部接口 ======================================
    // ====================================================================================

    /**
     * 监听消息
     * @param msg 消息
     * @param callback 回调
     * @param target 回调上下文
     * @param once 是否一次性监听，默认否
    */
    on(msg: string, callback: (...data: unknown[]) => void, target: unknown, once: boolean = false): void {
        if (once === void 0) once = false
        this._eventHandlers[msg] = this._eventHandlers[msg] || []
        this._eventHandlers[msg].push({
            callback: callback,
            target: target,
            once: once,
        })
    }

    /**
     * 注销监听消息
     * @param msg 消息
     * @param callback 回调
     * @param target 回调上下文
     */
    off(msg: string, callback: (...data: unknown[]) => void, target: unknown): void {
        let events = this._eventHandlers[msg]
        if (events && events.length > 0) {
            for (let i = events.length - 1; i >= 0; i--) {
                if (events[i] && events[i].callback === callback && events[i].target === target) {
                    events.splice(i, 1)

                    break
                }
            }
            if (events.length === 0) {
                delete this._eventHandlers[msg]
            }
        }
    }

    /**
     * 发出消息，过程中会检测，如果监听对象是cc.Component，但是未在场景中激活，则不会回调，并且会移除监听
     * @param msg 消息
     * @param data 数据
     */
    emit(msg: string, ...data: unknown[]): void {
        let events = this._eventHandlers[msg]
        if (events && events.length > 0) {
            for (let i = 0; i < events.length;) {
                let event = events[i]
                if (event.target) {
                    if (event.target instanceof cc.Component) {
                        event.target.node && event.callback.call(event.target, ...data)
                    } else {
                        event.callback.call(event.target, ...data)
                    }
                } else {
                    event.callback(...data)
                }
                if (event.once) {
                    events.splice(i, 1)
                } else {
                    i++
                }
            }
        }
    }

    /**
     * 查询消息是否有监听对象，过程中会检测，如果监听对象是cc.Component，但是未在场景中激活，会移除监听
     * @param msg 消息
     */
    hasListener(msg: string): boolean {
        let events = this._eventHandlers[msg],
            removeEvents = []
        events.forEach(event => {
            if (event.target instanceof cc.Component) {
                !event.target.node.activeInHierarchy && removeEvents.push(event)
            }
        })
        removeEvents.forEach(removeEvent => {
            for (let index = 0; index < events.length; index++) {
                const event = events[index]
                if (event === removeEvent) {
                    events.splice(index, 1)

                    break
                }
            }
        })

        return events && events.length > 0
    }
}